package com.spring.dict.maven;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


/**
 * @author liuzhiqiang
 */
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class DictAopAspect {

    public static final String VALUE = "";

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired(required = false)
    private DictService dictService;

    /**
     * 首字母大写
     *
     * @param str
     * @return
     */
    private static String getMethodName(String str) {
        String[] cs = str.split("");
        cs[0] = cs[0].toUpperCase();
        return String.join("", cs);
    }

    /**
     * 获取get方法
     *
     * @param c
     * @param field
     * @return
     */
    private static Method obtainGetMethod(Class c, Field field) {
        if (!field.getType().isPrimitive()) {
            return ReflectionUtils.findMethod(c, "get" + getMethodName(field.getName()));
        } else {
            return null;
        }
    }

    @PostConstruct
    public void init() {
        if (this.dictService == null) {
            this.dictService = new DictService() {
            };
        }
    }

    /**
     * 环绕通知
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution(* *..controller..*.*(..))")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        Object o = pjp.proceed();
        if (o instanceof Result) {
            Result result = (Result) o;
            Object o1 = dataHandle(result.getData());
            result.setData(o1);
            return result;
        } else {
            return o;
        }
    }

    public Pair<String, String> getKeyValue(String value, Field field, Dict dict) {
        String name = dict.value();
        String suffix = dict.suffix();
        if (StringUtils.isEmpty(name)) {
            name = field.getName();
        }
        if (StringUtils.equals(name, field.getName()) && StringUtils.isEmpty(suffix)) {
            suffix = DictConfiguration.SUFFIX;
        }
        String key = name + suffix;
        value = dictService.getDictLabel(value, dict.type());
        if (StringUtils.isEmpty(value)) {
            value = VALUE;
        }
        return Pair.of(key, value);
    }
    public Pair<String, List<String>> getKeyValues(List<String> values, Field field, Dict dict) {
        String name = dict.value();
        String suffix = dict.suffix();
        if (StringUtils.isEmpty(name)) {
            name = field.getName();
        }
        if (StringUtils.equals(name, field.getName()) && StringUtils.isEmpty(suffix)) {
            suffix = DictConfiguration.SUFFIX;
        }
        String key = name + suffix;

        List<String> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(values)) {
            for (String value : values) {
                String dictLabel = dictService.getDictLabel(value, dict.type());
                list.add(dictLabel);
            }
        }
        return Pair.of(key, list);
    }
    public Object dataHandle(Object data) {
        if (data != null) {
            if (data instanceof Collection) {
                List list = (List) data;
                List dataList = new ArrayList();
                for (Object o : list) {
                    dataList.add(dataHandle(o));
                }
                return dataList;
            } else if (data instanceof Map) {
                Map map = new HashMap(15);
                ((Map) data).forEach((key, value) -> map.put(key, dataHandle(value)));
                return map;
            } else if (filter(data.getClass().getName())) {
                String json = null;
                try {
                    json = objectMapper.writeValueAsString(data);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                Object obj = JSON.parse(json);
                if (obj instanceof JSONObject) {
                    JSONObject jsonObject = (JSONObject) obj;
                    Field[] fields = FieldUtils.getAllFields(data.getClass());
                    if (fields != null) {
                        for (Field field : fields) {
                            Object o = null;
                            Method method = obtainGetMethod(data.getClass(), field);
                            if (method != null) {
                                try {
                                    o = method.invoke(data);
                                } catch (Exception e) {
                                    log.error("反射异常:{}", e);
                                }
                            } else {
                                o = jsonObject.get(field.getName());
                            }
                            Dict dict = field.getAnnotation(Dict.class);
                            if (dict != null) {
                                if (field.getType() == String.class) {
                                    Pair<String, String> pair = getKeyValue((String) o, field, dict);
                                    jsonObject.put(pair.getKey(), pair.getValue());
                                } else if (field.getType() == List.class) {
                                    Pair<String, String> pair = getKeyValues((List) o, field, dict);
                                    jsonObject.put(pair.getKey(), pair.getValue());
                                }
                            }
                            if (o != null) {
                                jsonObject.put(field.getName(), dataHandle(o));
                            } else {
                                o = dictService.nullHandle(field);
                                jsonObject.put(field.getName(), o);
                            }
                        }
                    }
                    return jsonObject;
                }
            }
        }

        return data;
    }


    /**
     * 过滤
     *
     * @param className
     * @return
     */
    public Boolean filter(String className) {
        if (DictConfiguration.packagePath != null) {
            for (String packagePath : DictConfiguration.packagePath) {
                if (StringUtils.startsWith(className, packagePath)) {
                    return true;
                }
            }
        }
        return false;
    }
}
